QUESTION: Where and when are SHS in Apra Harbor?
AIMS: Analyse and plot SHS eDNA detections in Blue Hole, Inner Harbor, Middle Shoals, Orote Point and Sasa Bay over the period of Feb 2019 to July 2020.
## LOAD LIBRARIES ----
library(zoo) ## as.yearmon
library(ggmap) ## maps
library(ggsn) ## scale bar
library(viridis) ## colour blind friendly colour scale
library(ggspatial) ## north arrow
library(mgcv) ## GAM
library(gratia) ## as above
library(mgcViz) ## gam diagnostics
library(ggpubr) ## ggarrange
library(tidyverse) ## load last
## IMPORT SHS SURVEY DATA ----
## colour scheme
# scales::show_col(viridis(5))
# scales::show_col(viridis(5, alpha=.8))
# scales::show_col(viridis(4, alpha=.8, option="B"))
## import qPCR results (as confirmed by sanger sequencing)
SHS_data_unmod <- read.csv("docs/00.1_confirmed_detection_data.csv")
## import field sample data (including eDNA concentration)
SHS_field_sample_data_unmod <- read.csv("docs/00.2_field_sample_data.csv")
## CALCULATE PROPORTIONS ----
## calculate the proportion of technical replicates amplified per biological replicate (eDNA sample)
## and the proportion of biological replicates (eDNA samples) amplified per site
SHS_data<-SHS_data_unmod %>%
## group by month, site and sample
group_by(field_collection_month, site, sample) %>%
## add a column containing the tech rep number
mutate(tech_rep_no = row_number()) %>%
## add an unweighted tally of the number of rows per sample
## i.e. the number of tech reps run per sample
add_tally(name="no_tech_reps_run_per_sample") %>%
## add a weighted tally of the number of rows with a positive result
## i.e. the number of tech reps amplified per sample
add_tally(name="no_tech_reps_ampd_per_sample", wt=positive) %>%
## calculate the prop of tech reps amplified per sample
mutate(prop_tech_reps_ampd_per_sample =
1/no_tech_reps_run_per_sample*no_tech_reps_ampd_per_sample) %>%
## calculate the same information for biological replicates
left_join(., SHS_data_unmod %>%
## select only necessary information
select(sample, field_collection_month, site, positive) %>%
## filter out blanks
filter(!grepl('E', sample)) %>%
## retain only unique rows (removes rows where more than 1 tech rep amp'd)
unique(.) %>%
## group by month and site
group_by(field_collection_month, site) %>%
## add a weighted tally of the number of rows per month/site w positive results
## i.e. the number of biological reps amplified per sample
add_tally(name="no_bio_reps_ampd_per_site", wt=positive) %>%
## remove positive column (because it differs between bio reps)
select(-positive) %>%
## retain only unique rows (removes rows where info differs within sample)
unique(.) %>%
## add an unweighted tally of the number of rows per site
## i.e. the number of biological reps collected per site
add_tally(name="no_bio_reps_collected_per_site") %>%
## calculate the prop of biological reps amplified per site
mutate(prop_bio_reps_ampd_per_site =
1/no_bio_reps_collected_per_site*no_bio_reps_ampd_per_site),
## specify columns to join by
by = c("sample", "field_collection_month", "site"))
## MERGE DETECTIONS AND SAMPLE DATA AND ADD TIME OF YEAR INFO ----
SHS_data_full <- left_join(SHS_data, SHS_field_sample_data_unmod,
by = c("sample", "field_collection_month", "site"="site_name")) %>%
## rename dat column
rename(date_collected=date) %>%
## ungroup data
ungroup(.) %>%
## format field collection month column
mutate(field_collection_month = as.yearmon(field_collection_month)) %>%
## arrange rows by field collection month column values
arrange(., field_collection_month) %>%
## add and format extra date information columns
mutate(fc_month_factor = fct_inorder(factor(field_collection_month)),
fc_month_number = as.numeric(factor(field_collection_month)),
calendar_month = format(as.yearmon(field_collection_month), "%B"),
calendar_month_number = as.numeric(format(field_collection_month, "%m")),
fc_month_number_cyclic = ifelse(calendar_month_number > 2,
calendar_month_number-2,
calendar_month_number+10),
## add seasons to data frame (dry = December to May, wet = June to November)
season = ifelse(grepl("Dec", field_collection_month), "wet",
## Dec = transitional month
ifelse(grepl("Jan", field_collection_month), "dry",
ifelse(grepl("Feb", field_collection_month), "dry",
ifelse(grepl("Mar", field_collection_month), "dry",
ifelse(grepl("Apr", field_collection_month), "dry",
ifelse(grepl("May", field_collection_month), "dry",
ifelse(grepl("Jun", field_collection_month), "dry",
## Jun = transitional month
ifelse(grepl("Jul", field_collection_month), "wet",
ifelse(grepl("Aug", field_collection_month), "wet",
ifelse(grepl("Sep", field_collection_month), "wet",
ifelse(grepl("Oct", field_collection_month), "wet",
ifelse(grepl("Nov", field_collection_month), "wet",
"None")))))))))))),
season_factor = factor(season)) %>%
## arrange data by calendar month number
arrange(., calendar_month_number) %>%
## reorder factor levels by first appearance
mutate(calendar_month_factor = fct_inorder(factor(calendar_month))) %>%
## rename "positive" column to indicate tech rep was positive
rename(positive_tech = positive) %>%
## add a positive bio column to indicate bio rep is considered positive
mutate(positive_bio = ifelse(no_tech_reps_ampd_per_sample > 0, 1, 0)) %>%
## start mid year
mutate(calendar_month_factor_mid = factor(calendar_month_factor,
levels = c("July", "August", "September",
"October", "November", "December",
"January" , "February", "March",
"April", "May", "June")))
## export it
write.csv(SHS_data_full, "docs/01.0_SHS_data_full.csv", row.names = FALSE)
## extract all points (where recorded) for filter sample each site/month
sampling_points<-SHS_field_sample_data_unmod %>%
filter(latitude!="NA" | longitude!="NA") %>%
filter(latitude<13.48) %>% # blue hole point not at blue hole
mutate(field_collection_month=as.yearmon(field_collection_month)) %>%
select(site_name, field_collection_month, sample_no, latitude, longitude)
## extract points for first filter sample each site/month
first_sampling_points <- sampling_points %>%
filter(., sample_no == "1") # retain only first sample (and GPS point for that sample)
## export them
write.csv(sampling_points, "docs/01.1_sampling_points.csv", row.names = FALSE)
write.csv(first_sampling_points , "docs/01.2_first_sampling_points.csv", row.names = FALSE)
## IMPORT LIBRARIES AND MAP DATA ----
## provide ggmap with your key
# register_google(key = "[your key here]")
## create data frame for site locations
SHS_sites <- cbind.data.frame(site=c("Inner Harbor", "Sasa Bay", "Middle Shoals",
"Orote Point", "Blue Hole"),
m_latitude=c(13.43181, 13.44769, 13.44959,
13.44947, 13.43627),
m_longitude=c(144.67573, 144.67537, 144.65729,
144.62466, 144.62741))
## FORMAT SHS SURVEY DATA FOR MAP ----
## create data frame with site-level detection data (relevant to map) only
SHS_figure_map_data <- SHS_data_full %>%
## add site locations to full data set
left_join(., SHS_sites, by="site") %>%
## retain only information related to biological replicates (samples)
select(field_collection_month, date_collected,
site, m_latitude, m_longitude, calendar_month_factor,
calendar_month_factor_mid, calendar_month_number,
sample_less, prop_bio_reps_ampd_per_site,
no_bio_reps_ampd_per_site, season) %>%
## filter out blanks
filter(!grepl('E', sample_less)) %>%
## keep only unique rows (remove tech rep info)
unique(.) %>%
## group by month and site
group_by(calendar_month_factor, site) %>%
## calculate mean proportion of biological replicates amplified
## (because we sampled May and July of 2019 an 2020)
mutate(mean_prop_bio_amps = mean(prop_bio_reps_ampd_per_site),
mean_no_bio_amps = ceiling(mean(no_bio_reps_ampd_per_site)),
## specify the shape is an "x" if 0 and a "circle" if else
shapes = if_else(mean_no_bio_amps == 0, "4", "21"),
## specify label as nothing if 0 and the proportion rounded to 1 digit if else
labels = if_else(mean_no_bio_amps == 0, " ",
paste(mean_no_bio_amps))) %>%
## remove duplicate month, year and sample info
select(-prop_bio_reps_ampd_per_site, -no_bio_reps_ampd_per_site,
-field_collection_month, -date_collected, -sample_less) %>%
unique(.) %>%
## for scale_bar
mutate(long = m_longitude, lat = m_latitude)
## MAKE A MAP ----
# ## Find latitudes and longitudes to be centre of distribution map
# mean(SHS_sites$m_latitude) # 13.44297
# mean(SHS_sites$m_longitude) # 144.6521
## create map style at https://mapstyle.withgoogle.com/
s2<- "style=element:geometry%7Ccolor:0xf5f5f5&style=element:labels%7Cvisibility:off&style=element:labels.icon%7Cvisibility:off&style=element:labels.text.fill%7Ccolor:0x616161&style=element:labels.text.stroke%7Ccolor:0xf5f5f5&style=feature:administrative%7Celement:geometry%7Cvisibility:off&style=feature:administrative.land_parcel%7Cvisibility:off&style=feature:administrative.land_parcel%7Celement:labels.text.fill%7Ccolor:0xbdbdbd&style=feature:administrative.neighborhood%7Cvisibility:off&style=feature:poi%7Cvisibility:off&style=feature:poi%7Celement:geometry%7Ccolor:0xeeeeee&style=feature:poi%7Celement:labels.text.fill%7Ccolor:0x757575&style=feature:poi.park%7Celement:geometry%7Ccolor:0xe5e5e5&style=feature:poi.park%7Celement:labels.text.fill%7Ccolor:0x9e9e9e&style=feature:road%7Cvisibility:off&style=feature:road%7Celement:geometry%7Ccolor:0xffffff&style=feature:road%7Celement:labels.icon%7Cvisibility:off&style=feature:road.arterial%7Celement:labels.text.fill%7Ccolor:0x757575&style=feature:road.highway%7Celement:geometry%7Ccolor:0xdadada&style=feature:road.highway%7Celement:labels.text.fill%7Ccolor:0x616161&style=feature:road.local%7Celement:labels.text.fill%7Ccolor:0x9e9e9e&style=feature:transit%7Cvisibility:off&style=feature:transit.line%7Celement:geometry%7Ccolor:0xe5e5e5&style=feature:transit.station%7Celement:geometry%7Ccolor:0xeeeeee&style=feature:water%7Celement:geometry%7Ccolor:0xc9c9c9&style=feature:water%7Celement:labels.text.fill%7Ccolor:0x9e9e9e&size=480x360"
## get a Google map of Apra Harbor (retrieves raster map from Google Maps)
## Note that in most cases by using this function you are agreeing to the Google Maps API Terms of Service at https://cloud.google.com/maps-platform/terms/ **
ApraHarbor <- get_googlemap(center = c(lon = 144.6521, lat = 13.44297),
zoom = 13,
maptype = 'terrain',
color = 'bw',
style = s2)
## use ggmap to plot the the raster using the ggplot2 framework
ggmap(ApraHarbor)

## add SHS survey information to plot
map1.0 <- ggmap(ApraHarbor, extent = "panel") +
## specify the aesthetics of the "bubbles" on the plot
geom_point(aes(x = m_longitude, y = m_latitude,
fill = site,
size = mean_no_bio_amps, shape = shapes),
data = SHS_figure_map_data,
alpha = 0.6,
stroke = 0.6) +
## customise the size
scale_size(range = c(2, 12), guide = "none") +
## customise the shape so that 0 values are "x"
scale_shape_manual(values=c(21, 4), guide = "none") +
# limit the size of the plot (produces message, ignore it)
scale_x_continuous(limits = c(144.615, 144.69), expand = c(0, 0)) +
scale_y_continuous(limits = c(13.415, 13.47), expand = c(0, 0)) +
xlab("Longitude")+
ylab("Latitude")+
## facet the plot by month (n=2 for May and July)
facet_wrap(~ calendar_month_factor_mid, ncol = 3) +
## move the legend and edit the facet strip panels
theme(legend.position="bottom",
strip.background = element_blank(),
panel.border = element_rect(colour = "gray30", fill = NA),
strip.text.x = element_text(margin = margin(0.1, 0, 0.1, 0, "cm"))) +
## make it colour blind friendly
scale_fill_viridis(discrete=TRUE) +
## edit legend appearance
guides(fill = guide_legend(override.aes = list(shape = 21,
size = 5),
title = "Site")) +
## add labels to "bubbles"
geom_text(aes(x = m_longitude, y = m_latitude,
label = labels,
size = 0.1),
data = SHS_figure_map_data)
# map1.0
# plot.new() ## use this if you get an error below
## add a scale bar
map1.2 <- map1.0 + scalebar(data = SHS_figure_map_data,
dist=0.5, # distance to represent each segment of the scale bar
dist_unit="km", # unit of measurement for dist
transform=TRUE, # TRUE = decimal degrees, FALSE = m
model = 'WGS84', # choice of ellipsoide model (where transorm=TRUE)
border.size = 0.2,
box.color = "grey30",
box.fill = c("grey80", "white"),
st.size = 2, ## scale bar size
height = 0.09, ## height as proportion of the y-axis (0-1)
location = "bottomright", ## scale bars location in the plot
anchor = c(x=144.688,y=13.417), ## for corner of location
st.dist = 0.1, ## distance bw scale bar and text
alpha = 0 ## make the text see-through
)
# map1.2
## add north arrow
map1.3 <- map1.2 +
annotation_north_arrow(height = unit(0.25, "cm"),
width = unit(0.25,"cm"),
location = "tl", ## top left
pad_x = unit(0.25, "cm"),
pad_y = unit(0.25, "cm"),
style = north_arrow_orienteering(
line_width = 1,
line_col = "gray50",
fill = c("gray80", "white"),
text_col = NA, ## removes text?
text_family = "",
text_face = NULL,
text_size = 0,
text_angle = 0))
map1.3

## save it
ggsave("docs/figure_detect.tiff", plot=map1.3, device="tiff", width=21/1.2, height=28/1.2, units="cm", dpi=500)
## FORMAT SHS DATA FOR BUBBLE PLOT MODEL ----
## create data frame with site-level detection data (relevant to model) only
SHS_bubble_data <- SHS_data_full %>%
## filter out blanks
filter(!grepl('E', sample_less)) %>%
## retain only information at the site-level
select(field_collection_month, fc_month_factor, fc_month_number,
fc_month_number_cyclic, calendar_month, calendar_month_factor,
calendar_month_factor_mid, calendar_month_number, date_collected,
site, season, season_factor, prop_bio_reps_ampd_per_site,
no_bio_reps_ampd_per_site, no_bio_reps_collected_per_site) %>%
## keep only unique rows (remove tech rep info)
unique(.) %>%
mutate(site_factor=factor(site)) %>%
## create new calendar month number column
mutate(calendar_month_number_mid = as.numeric(calendar_month_factor_mid))
## export it
write.csv(SHS_bubble_data, "docs/01.3_SHS_bubble_data.csv", row.names = FALSE)
## MODEL DATA WITH GAM ----
gam_bubble_count<-gam(no_bio_reps_ampd_per_site ~
site_factor +
s(calendar_month_number_mid, bs="cc"),
offset = no_bio_reps_collected_per_site, ## all are n = 10
method = "REML",
family = poisson(), ## suitable for count data
data = SHS_bubble_data)
## check diagnostics
print(check(getViz(gam_bubble_count)))
Method: REML Optimizer: outer newton
full convergence after 3 iterations.
Gradient range [-1.664265e-06,-1.664265e-06]
(score 68.30349 & scale 1).
Hessian positive definite, eigenvalue range [0.4266786,0.4266786].
Model rank = 13 / 13
Basis dimension (k) checking results. Low p-value (k-index<1) may
indicate that k is too low, especially if edf is close to k'.
k' edf k-index p-value
s(calendar_month_number_mid) 8.00 4.06 0.94 0.49

summary_gbc<-summary(gam_bubble_count)
summary_gbc
Family: poisson
Link function: log
Formula:
no_bio_reps_ampd_per_site ~ site_factor + s(calendar_month_number_mid,
bs = "cc")
Parametric coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -12.1776 0.7121 -17.100 < 2e-16 ***
site_factorInner Harbor 2.9479 0.7267 4.057 4.98e-05 ***
site_factorMiddle Shoals 0.3746 0.9134 0.410 0.6817
site_factorOrote Point 0.3452 0.9132 0.378 0.7055
site_factorSasa Bay 1.7609 0.7643 2.304 0.0212 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Approximate significance of smooth terms:
edf Ref.df Chi.sq p-value
s(calendar_month_number_mid) 4.065 8 26.4 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
R-sq.(adj) = 0.695 Deviance explained = 60.2%
-REML = 68.303 Scale est. = 1 n = 71
## Parametric coefficients:
knitr::kable(summary_gbc$p.table, digits = 4)
| (Intercept) |
-12.1776 |
0.7121 |
-17.1000 |
0.0000 |
| site_factorInner Harbor |
2.9479 |
0.7267 |
4.0567 |
0.0000 |
| site_factorMiddle Shoals |
0.3746 |
0.9134 |
0.4101 |
0.6817 |
| site_factorOrote Point |
0.3452 |
0.9132 |
0.3780 |
0.7055 |
| site_factorSasa Bay |
1.7609 |
0.7643 |
2.3038 |
0.0212 |
## Approximate significance of smooth terms:
knitr::kable(summary_gbc$s.table, digits = 4)
| s(calendar_month_number_mid) |
4.065 |
8 |
26.4022 |
0 |
## visualise GAM results
print(draw(gam_bubble_count))

LS0tCnRpdGxlOiAiU0hTIHN1cnZleSByZXN1bHRzIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDogZGVmYXVsdAotLS0KClFVRVNUSU9OOiBXaGVyZSBhbmQgd2hlbiBhcmUgU0hTIGluIEFwcmEgSGFyYm9yPwoKQUlNUzogQW5hbHlzZSBhbmQgcGxvdCBTSFMgZUROQSBkZXRlY3Rpb25zIGluIEJsdWUgSG9sZSwgSW5uZXIgSGFyYm9yLCBNaWRkbGUgU2hvYWxzLCBPcm90ZSBQb2ludCBhbmQgU2FzYSBCYXkgb3ZlciB0aGUgcGVyaW9kIG9mIEZlYiAyMDE5IHRvIEp1bHkgMjAyMC4gCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KY3VycmVudF9kaXJlY3RvcnkgPC0gZGlybmFtZShyc3R1ZGlvYXBpOjpnZXRBY3RpdmVEb2N1bWVudENvbnRleHQoKSRwYXRoKQprbml0cjo6b3B0c19rbml0JHNldChyb290LmRpcj1ub3JtYWxpemVQYXRoKHBhc3RlKGN1cnJlbnRfZGlyZWN0b3J5KSkpCmBgYAoKYGBge3IgbG9hZF9saWJyYXJpZXMsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CiMjIExPQUQgTElCUkFSSUVTIC0tLS0KbGlicmFyeSh6b28pICMjIGFzLnllYXJtb24KbGlicmFyeShnZ21hcCkgIyMgbWFwcwpsaWJyYXJ5KGdnc24pICMjIHNjYWxlIGJhcgpsaWJyYXJ5KHZpcmlkaXMpICMjIGNvbG91ciBibGluZCBmcmllbmRseSBjb2xvdXIgc2NhbGUKbGlicmFyeShnZ3NwYXRpYWwpICMjIG5vcnRoIGFycm93CmxpYnJhcnkobWdjdikgIyMgR0FNCmxpYnJhcnkoZ3JhdGlhKSAjIyBhcyBhYm92ZQpsaWJyYXJ5KG1nY1ZpeikgIyMgZ2FtIGRpYWdub3N0aWNzCmxpYnJhcnkoZ2dwdWJyKSAjIyBnZ2FycmFuZ2UKbGlicmFyeSh0aWR5dmVyc2UpICMjIGxvYWQgbGFzdApgYGAKCgpgYGB7ciBTSFNfc3VydmV5X2RhdGEsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQojIyBJTVBPUlQgU0hTIFNVUlZFWSBEQVRBIC0tLS0KCiMjIGNvbG91ciBzY2hlbWUKIyBzY2FsZXM6OnNob3dfY29sKHZpcmlkaXMoNSkpCiMgc2NhbGVzOjpzaG93X2NvbCh2aXJpZGlzKDUsIGFscGhhPS44KSkKIyBzY2FsZXM6OnNob3dfY29sKHZpcmlkaXMoNCwgYWxwaGE9LjgsIG9wdGlvbj0iQiIpKQoKIyMgaW1wb3J0IHFQQ1IgcmVzdWx0cyAoYXMgY29uZmlybWVkIGJ5IHNhbmdlciBzZXF1ZW5jaW5nKQpTSFNfZGF0YV91bm1vZCA8LSByZWFkLmNzdigiZG9jcy8wMC4xX2NvbmZpcm1lZF9kZXRlY3Rpb25fZGF0YS5jc3YiKQoKIyMgaW1wb3J0IGZpZWxkIHNhbXBsZSBkYXRhIChpbmNsdWRpbmcgZUROQSBjb25jZW50cmF0aW9uKQpTSFNfZmllbGRfc2FtcGxlX2RhdGFfdW5tb2QgPC0gcmVhZC5jc3YoImRvY3MvMDAuMl9maWVsZF9zYW1wbGVfZGF0YS5jc3YiKQoKIyMgQ0FMQ1VMQVRFIFBST1BPUlRJT05TIC0tLS0KCiMjIGNhbGN1bGF0ZSB0aGUgcHJvcG9ydGlvbiBvZiB0ZWNobmljYWwgcmVwbGljYXRlcyBhbXBsaWZpZWQgcGVyIGJpb2xvZ2ljYWwgcmVwbGljYXRlIChlRE5BIHNhbXBsZSkKIyMgYW5kIHRoZSBwcm9wb3J0aW9uIG9mIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyAoZUROQSBzYW1wbGVzKSBhbXBsaWZpZWQgcGVyIHNpdGUKU0hTX2RhdGE8LVNIU19kYXRhX3VubW9kICU+JQogICMjIGdyb3VwIGJ5IG1vbnRoLCBzaXRlIGFuZCBzYW1wbGUKICBncm91cF9ieShmaWVsZF9jb2xsZWN0aW9uX21vbnRoLCBzaXRlLCBzYW1wbGUpICU+JSAKICAjIyBhZGQgYSBjb2x1bW4gY29udGFpbmluZyB0aGUgdGVjaCByZXAgbnVtYmVyIAogIG11dGF0ZSh0ZWNoX3JlcF9ubyA9IHJvd19udW1iZXIoKSkgJT4lCiAgIyMgYWRkIGFuIHVud2VpZ2h0ZWQgdGFsbHkgb2YgdGhlIG51bWJlciBvZiByb3dzIHBlciBzYW1wbGUKICAjIyBpLmUuIHRoZSBudW1iZXIgb2YgdGVjaCByZXBzIHJ1biBwZXIgc2FtcGxlCiAgYWRkX3RhbGx5KG5hbWU9Im5vX3RlY2hfcmVwc19ydW5fcGVyX3NhbXBsZSIpICU+JSAKICAjIyBhZGQgYSB3ZWlnaHRlZCB0YWxseSBvZiB0aGUgbnVtYmVyIG9mIHJvd3Mgd2l0aCBhIHBvc2l0aXZlIHJlc3VsdAogICMjIGkuZS4gdGhlIG51bWJlciBvZiB0ZWNoIHJlcHMgYW1wbGlmaWVkIHBlciBzYW1wbGUKICBhZGRfdGFsbHkobmFtZT0ibm9fdGVjaF9yZXBzX2FtcGRfcGVyX3NhbXBsZSIsIHd0PXBvc2l0aXZlKSAlPiUgCiAgIyMgY2FsY3VsYXRlIHRoZSAgcHJvcCBvZiB0ZWNoIHJlcHMgYW1wbGlmaWVkIHBlciBzYW1wbGUKICBtdXRhdGUocHJvcF90ZWNoX3JlcHNfYW1wZF9wZXJfc2FtcGxlID0gCiAgICAgICAgICAgMS9ub190ZWNoX3JlcHNfcnVuX3Blcl9zYW1wbGUqbm9fdGVjaF9yZXBzX2FtcGRfcGVyX3NhbXBsZSkgJT4lCiAgIyMgY2FsY3VsYXRlIHRoZSBzYW1lIGluZm9ybWF0aW9uIGZvciBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMKICBsZWZ0X2pvaW4oLiwgU0hTX2RhdGFfdW5tb2QgJT4lCiAgICAgICAgICAgICAgIyMgc2VsZWN0IG9ubHkgbmVjZXNzYXJ5IGluZm9ybWF0aW9uCiAgICAgICAgICAgICAgc2VsZWN0KHNhbXBsZSwgZmllbGRfY29sbGVjdGlvbl9tb250aCwgc2l0ZSwgcG9zaXRpdmUpICU+JQogICAgICAgICAgICAgICMjIGZpbHRlciBvdXQgYmxhbmtzCiAgICAgICAgICAgICAgZmlsdGVyKCFncmVwbCgnRScsIHNhbXBsZSkpICU+JQogICAgICAgICAgICAgICMjIHJldGFpbiBvbmx5IHVuaXF1ZSByb3dzIChyZW1vdmVzIHJvd3Mgd2hlcmUgbW9yZSB0aGFuIDEgdGVjaCByZXAgYW1wJ2QpCiAgICAgICAgICAgICAgdW5pcXVlKC4pICU+JQogICAgICAgICAgICAgICMjIGdyb3VwIGJ5IG1vbnRoIGFuZCBzaXRlCiAgICAgICAgICAgICAgZ3JvdXBfYnkoZmllbGRfY29sbGVjdGlvbl9tb250aCwgc2l0ZSkgJT4lCiAgICAgICAgICAgICAgIyMgYWRkIGEgd2VpZ2h0ZWQgdGFsbHkgb2YgdGhlIG51bWJlciBvZiByb3dzIHBlciBtb250aC9zaXRlIHcgcG9zaXRpdmUgcmVzdWx0cwogICAgICAgICAgICAgICMjIGkuZS4gdGhlIG51bWJlciBvZiBiaW9sb2dpY2FsIHJlcHMgYW1wbGlmaWVkIHBlciBzYW1wbGUKICAgICAgICAgICAgICBhZGRfdGFsbHkobmFtZT0ibm9fYmlvX3JlcHNfYW1wZF9wZXJfc2l0ZSIsIHd0PXBvc2l0aXZlKSAlPiUKICAgICAgICAgICAgICAjIyByZW1vdmUgcG9zaXRpdmUgY29sdW1uIChiZWNhdXNlIGl0IGRpZmZlcnMgYmV0d2VlbiBiaW8gcmVwcykKICAgICAgICAgICAgICBzZWxlY3QoLXBvc2l0aXZlKSAlPiUKICAgICAgICAgICAgICAjIyByZXRhaW4gb25seSB1bmlxdWUgcm93cyAocmVtb3ZlcyByb3dzIHdoZXJlIGluZm8gZGlmZmVycyB3aXRoaW4gc2FtcGxlKQogICAgICAgICAgICAgIHVuaXF1ZSguKSAlPiUKICAgICAgICAgICAgICAjIyBhZGQgYW4gdW53ZWlnaHRlZCB0YWxseSBvZiB0aGUgbnVtYmVyIG9mIHJvd3MgcGVyIHNpdGUKICAgICAgICAgICAgICAjIyBpLmUuIHRoZSBudW1iZXIgb2YgYmlvbG9naWNhbCByZXBzIGNvbGxlY3RlZCBwZXIgc2l0ZQogICAgICAgICAgICAgIGFkZF90YWxseShuYW1lPSJub19iaW9fcmVwc19jb2xsZWN0ZWRfcGVyX3NpdGUiKSAlPiUKICAgICAgICAgICAgICAjIyBjYWxjdWxhdGUgdGhlIHByb3Agb2YgYmlvbG9naWNhbCByZXBzIGFtcGxpZmllZCBwZXIgc2l0ZQogICAgICAgICAgICAgIG11dGF0ZShwcm9wX2Jpb19yZXBzX2FtcGRfcGVyX3NpdGUgPQogICAgICAgICAgICAgICAgICAgICAgIDEvbm9fYmlvX3JlcHNfY29sbGVjdGVkX3Blcl9zaXRlKm5vX2Jpb19yZXBzX2FtcGRfcGVyX3NpdGUpLAogICAgICAgICAgICAjIyBzcGVjaWZ5IGNvbHVtbnMgdG8gam9pbiBieQogICAgICAgICAgICBieSA9IGMoInNhbXBsZSIsICJmaWVsZF9jb2xsZWN0aW9uX21vbnRoIiwgInNpdGUiKSkgCgojIyBNRVJHRSBERVRFQ1RJT05TIEFORCBTQU1QTEUgREFUQSBBTkQgQUREIFRJTUUgT0YgWUVBUiBJTkZPIC0tLS0KU0hTX2RhdGFfZnVsbCA8LSBsZWZ0X2pvaW4oU0hTX2RhdGEsIFNIU19maWVsZF9zYW1wbGVfZGF0YV91bm1vZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygic2FtcGxlIiwgImZpZWxkX2NvbGxlY3Rpb25fbW9udGgiLCAic2l0ZSI9InNpdGVfbmFtZSIpKSAlPiUKICAjIyByZW5hbWUgZGF0IGNvbHVtbgogIHJlbmFtZShkYXRlX2NvbGxlY3RlZD1kYXRlKSAlPiUKICAjIyB1bmdyb3VwIGRhdGEKICB1bmdyb3VwKC4pICU+JQogICMjIGZvcm1hdCBmaWVsZCBjb2xsZWN0aW9uIG1vbnRoIGNvbHVtbgogIG11dGF0ZShmaWVsZF9jb2xsZWN0aW9uX21vbnRoID0gYXMueWVhcm1vbihmaWVsZF9jb2xsZWN0aW9uX21vbnRoKSkgJT4lCiAgIyMgYXJyYW5nZSByb3dzIGJ5IGZpZWxkIGNvbGxlY3Rpb24gbW9udGggY29sdW1uIHZhbHVlcwogIGFycmFuZ2UoLiwgZmllbGRfY29sbGVjdGlvbl9tb250aCkgJT4lCiAgIyMgYWRkIGFuZCBmb3JtYXQgZXh0cmEgZGF0ZSBpbmZvcm1hdGlvbiBjb2x1bW5zCiAgbXV0YXRlKGZjX21vbnRoX2ZhY3RvciA9IGZjdF9pbm9yZGVyKGZhY3RvcihmaWVsZF9jb2xsZWN0aW9uX21vbnRoKSksCiAgICAgICAgIGZjX21vbnRoX251bWJlciA9IGFzLm51bWVyaWMoZmFjdG9yKGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpKSwKICAgICAgICAgY2FsZW5kYXJfbW9udGggPSBmb3JtYXQoYXMueWVhcm1vbihmaWVsZF9jb2xsZWN0aW9uX21vbnRoKSwgIiVCIiksCiAgICAgICAgIGNhbGVuZGFyX21vbnRoX251bWJlciA9IGFzLm51bWVyaWMoZm9ybWF0KGZpZWxkX2NvbGxlY3Rpb25fbW9udGgsICIlbSIpKSwKICAgICAgICAgZmNfbW9udGhfbnVtYmVyX2N5Y2xpYyA9IGlmZWxzZShjYWxlbmRhcl9tb250aF9udW1iZXIgPiAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjYWxlbmRhcl9tb250aF9udW1iZXItMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FsZW5kYXJfbW9udGhfbnVtYmVyKzEwKSwKICAgICAgICAgIyMgYWRkIHNlYXNvbnMgdG8gZGF0YSBmcmFtZSAoZHJ5ID0gRGVjZW1iZXIgdG8gTWF5LCB3ZXQgPSBKdW5lIHRvIE5vdmVtYmVyKQogICAgICAgICBzZWFzb24gPSBpZmVsc2UoZ3JlcGwoIkRlYyIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAid2V0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAjIyBEZWMgPSB0cmFuc2l0aW9uYWwgbW9udGgKICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkphbiIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAiZHJ5IiwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiRmViIiwgZmllbGRfY29sbGVjdGlvbl9tb250aCksICJkcnkiLAogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIk1hciIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAiZHJ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkFwciIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAiZHJ5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiTWF5IiwgZmllbGRfY29sbGVjdGlvbl9tb250aCksICJkcnkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkp1biIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAiZHJ5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIyBKdW4gPSB0cmFuc2l0aW9uYWwgbW9udGgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIkp1bCIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAid2V0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShncmVwbCgiQXVnIiwgZmllbGRfY29sbGVjdGlvbl9tb250aCksICJ3ZXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIlNlcCIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAid2V0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIk9jdCIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAid2V0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZ3JlcGwoIk5vdiIsIGZpZWxkX2NvbGxlY3Rpb25fbW9udGgpLCAid2V0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOb25lIikpKSkpKSkpKSkpKSwKICAgICAgICAgc2Vhc29uX2ZhY3RvciA9IGZhY3RvcihzZWFzb24pKSAlPiUKICAjIyBhcnJhbmdlIGRhdGEgYnkgY2FsZW5kYXIgbW9udGggbnVtYmVyCiAgYXJyYW5nZSguLCBjYWxlbmRhcl9tb250aF9udW1iZXIpICU+JQogICMjIHJlb3JkZXIgZmFjdG9yIGxldmVscyBieSBmaXJzdCBhcHBlYXJhbmNlCiAgbXV0YXRlKGNhbGVuZGFyX21vbnRoX2ZhY3RvciA9IGZjdF9pbm9yZGVyKGZhY3RvcihjYWxlbmRhcl9tb250aCkpKSAlPiUKICAjIyByZW5hbWUgInBvc2l0aXZlIiBjb2x1bW4gdG8gaW5kaWNhdGUgdGVjaCByZXAgd2FzIHBvc2l0aXZlCiAgcmVuYW1lKHBvc2l0aXZlX3RlY2ggPSBwb3NpdGl2ZSkgJT4lCiAgIyMgYWRkIGEgcG9zaXRpdmUgYmlvIGNvbHVtbiB0byBpbmRpY2F0ZSBiaW8gcmVwIGlzIGNvbnNpZGVyZWQgcG9zaXRpdmUKICBtdXRhdGUocG9zaXRpdmVfYmlvID0gaWZlbHNlKG5vX3RlY2hfcmVwc19hbXBkX3Blcl9zYW1wbGUgPiAwLCAxLCAwKSkgJT4lCiAgIyMgc3RhcnQgbWlkIHllYXIKICBtdXRhdGUoY2FsZW5kYXJfbW9udGhfZmFjdG9yX21pZCA9IGZhY3RvcihjYWxlbmRhcl9tb250aF9mYWN0b3IsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiSnVseSIsICJBdWd1c3QiLCAiU2VwdGVtYmVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9jdG9iZXIiLCAiTm92ZW1iZXIiLCAiRGVjZW1iZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSmFudWFyeSIgLCAiRmVicnVhcnkiLCAiTWFyY2giLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQXByaWwiLCAiTWF5IiwgIkp1bmUiKSkpCgojIyBleHBvcnQgaXQKd3JpdGUuY3N2KFNIU19kYXRhX2Z1bGwsICJkb2NzLzAxLjBfU0hTX2RhdGFfZnVsbC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMjIGV4dHJhY3QgYWxsIHBvaW50cyAod2hlcmUgcmVjb3JkZWQpIGZvciBmaWx0ZXIgc2FtcGxlIGVhY2ggc2l0ZS9tb250aApzYW1wbGluZ19wb2ludHM8LVNIU19maWVsZF9zYW1wbGVfZGF0YV91bm1vZCAlPiUKICBmaWx0ZXIobGF0aXR1ZGUhPSJOQSIgfCBsb25naXR1ZGUhPSJOQSIpICU+JSAKICBmaWx0ZXIobGF0aXR1ZGU8MTMuNDgpICU+JSAjIGJsdWUgaG9sZSBwb2ludCBub3QgYXQgYmx1ZSBob2xlCiAgbXV0YXRlKGZpZWxkX2NvbGxlY3Rpb25fbW9udGg9YXMueWVhcm1vbihmaWVsZF9jb2xsZWN0aW9uX21vbnRoKSkgJT4lCiAgc2VsZWN0KHNpdGVfbmFtZSwgZmllbGRfY29sbGVjdGlvbl9tb250aCwgc2FtcGxlX25vLCBsYXRpdHVkZSwgbG9uZ2l0dWRlKQoKIyMgZXh0cmFjdCBwb2ludHMgZm9yIGZpcnN0IGZpbHRlciBzYW1wbGUgZWFjaCBzaXRlL21vbnRoCmZpcnN0X3NhbXBsaW5nX3BvaW50cyA8LSBzYW1wbGluZ19wb2ludHMgJT4lCiAgZmlsdGVyKC4sIHNhbXBsZV9ubyA9PSAiMSIpICMgcmV0YWluIG9ubHkgZmlyc3Qgc2FtcGxlIChhbmQgR1BTIHBvaW50IGZvciB0aGF0IHNhbXBsZSkKCiMjIGV4cG9ydCB0aGVtIAp3cml0ZS5jc3Yoc2FtcGxpbmdfcG9pbnRzLCAiZG9jcy8wMS4xX3NhbXBsaW5nX3BvaW50cy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKd3JpdGUuY3N2KGZpcnN0X3NhbXBsaW5nX3BvaW50cyAsICJkb2NzLzAxLjJfZmlyc3Rfc2FtcGxpbmdfcG9pbnRzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpgYGAKCmBgYHtyIFNIU19zdXJ2ZXlfbWFwcywgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIGZpZy53aWR0aCA9IDEyLCBmaWd1cmUuaGVpZ2h0ID0gMjB9CiMjIElNUE9SVCBMSUJSQVJJRVMgQU5EIE1BUCBEQVRBIC0tLS0KCiMjIHByb3ZpZGUgZ2dtYXAgd2l0aCB5b3VyIGtleQojIHJlZ2lzdGVyX2dvb2dsZShrZXkgPSAiW3lvdXIga2V5IGhlcmVdIikgCgojIyBjcmVhdGUgZGF0YSBmcmFtZSBmb3Igc2l0ZSBsb2NhdGlvbnMKU0hTX3NpdGVzIDwtIGNiaW5kLmRhdGEuZnJhbWUoc2l0ZT1jKCJJbm5lciBIYXJib3IiLCAiU2FzYSBCYXkiLCAiTWlkZGxlIFNob2FscyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9yb3RlIFBvaW50IiwgIkJsdWUgSG9sZSIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbV9sYXRpdHVkZT1jKDEzLjQzMTgxLCAxMy40NDc2OSwgMTMuNDQ5NTksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEzLjQ0OTQ3LCAxMy40MzYyNyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1fbG9uZ2l0dWRlPWMoMTQ0LjY3NTczLCAxNDQuNjc1MzcsIDE0NC42NTcyOSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDE0NC42MjQ2NiwgMTQ0LjYyNzQxKSkKCiMjIEZPUk1BVCBTSFMgU1VSVkVZIERBVEEgRk9SIE1BUCAtLS0tCgojIyBjcmVhdGUgZGF0YSBmcmFtZSB3aXRoIHNpdGUtbGV2ZWwgZGV0ZWN0aW9uIGRhdGEgKHJlbGV2YW50IHRvIG1hcCkgb25seQpTSFNfZmlndXJlX21hcF9kYXRhIDwtIFNIU19kYXRhX2Z1bGwgJT4lCiAgIyMgYWRkIHNpdGUgbG9jYXRpb25zIHRvIGZ1bGwgZGF0YSBzZXQKICBsZWZ0X2pvaW4oLiwgU0hTX3NpdGVzLCBieT0ic2l0ZSIpICU+JQogICMjIHJldGFpbiBvbmx5IGluZm9ybWF0aW9uIHJlbGF0ZWQgdG8gYmlvbG9naWNhbCByZXBsaWNhdGVzIChzYW1wbGVzKQogIHNlbGVjdChmaWVsZF9jb2xsZWN0aW9uX21vbnRoLCBkYXRlX2NvbGxlY3RlZCwKICAgICAgICAgc2l0ZSwgbV9sYXRpdHVkZSwgbV9sb25naXR1ZGUsIGNhbGVuZGFyX21vbnRoX2ZhY3RvciwKICAgICAgICAgY2FsZW5kYXJfbW9udGhfZmFjdG9yX21pZCwgY2FsZW5kYXJfbW9udGhfbnVtYmVyLAogICAgICAgICBzYW1wbGVfbGVzcywgcHJvcF9iaW9fcmVwc19hbXBkX3Blcl9zaXRlLCAKICAgICAgICAgbm9fYmlvX3JlcHNfYW1wZF9wZXJfc2l0ZSwgc2Vhc29uKSAlPiUKICAjIyBmaWx0ZXIgb3V0IGJsYW5rcwogIGZpbHRlcighZ3JlcGwoJ0UnLCBzYW1wbGVfbGVzcykpICU+JQogICMjIGtlZXAgb25seSB1bmlxdWUgcm93cyAocmVtb3ZlIHRlY2ggcmVwIGluZm8pCiAgdW5pcXVlKC4pICU+JQogICMjIGdyb3VwIGJ5IG1vbnRoIGFuZCBzaXRlCiAgZ3JvdXBfYnkoY2FsZW5kYXJfbW9udGhfZmFjdG9yLCBzaXRlKSAlPiUKICAjIyBjYWxjdWxhdGUgbWVhbiBwcm9wb3J0aW9uIG9mIGJpb2xvZ2ljYWwgcmVwbGljYXRlcyBhbXBsaWZpZWQgCiAgIyMgKGJlY2F1c2Ugd2Ugc2FtcGxlZCBNYXkgYW5kIEp1bHkgb2YgMjAxOSBhbiAyMDIwKQogIG11dGF0ZShtZWFuX3Byb3BfYmlvX2FtcHMgPSBtZWFuKHByb3BfYmlvX3JlcHNfYW1wZF9wZXJfc2l0ZSksIAogICAgICAgICBtZWFuX25vX2Jpb19hbXBzID0gY2VpbGluZyhtZWFuKG5vX2Jpb19yZXBzX2FtcGRfcGVyX3NpdGUpKSwKICAgICAgICAgIyMgc3BlY2lmeSB0aGUgc2hhcGUgaXMgYW4gIngiIGlmIDAgYW5kIGEgImNpcmNsZSIgaWYgZWxzZQogICAgICAgICBzaGFwZXMgPSBpZl9lbHNlKG1lYW5fbm9fYmlvX2FtcHMgPT0gMCwgIjQiLCAiMjEiKSwgCiAgICAgICAgICMjIHNwZWNpZnkgbGFiZWwgYXMgbm90aGluZyBpZiAwIGFuZCB0aGUgcHJvcG9ydGlvbiByb3VuZGVkIHRvIDEgZGlnaXQgaWYgZWxzZQogICAgICAgICBsYWJlbHMgPSBpZl9lbHNlKG1lYW5fbm9fYmlvX2FtcHMgPT0gMCwgIiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShtZWFuX25vX2Jpb19hbXBzKSkpICU+JQogICMjIHJlbW92ZSBkdXBsaWNhdGUgbW9udGgsIHllYXIgYW5kIHNhbXBsZSBpbmZvCiAgc2VsZWN0KC1wcm9wX2Jpb19yZXBzX2FtcGRfcGVyX3NpdGUsIC1ub19iaW9fcmVwc19hbXBkX3Blcl9zaXRlLAogICAgICAgICAtZmllbGRfY29sbGVjdGlvbl9tb250aCwgLWRhdGVfY29sbGVjdGVkLCAtc2FtcGxlX2xlc3MpICU+JQogIHVuaXF1ZSguKSAlPiUKICAjIyBmb3Igc2NhbGVfYmFyCiAgbXV0YXRlKGxvbmcgPSBtX2xvbmdpdHVkZSwgbGF0ID0gbV9sYXRpdHVkZSkKCiMjIE1BS0UgQSBNQVAgLS0tLQoKIyAjIyBGaW5kIGxhdGl0dWRlcyBhbmQgbG9uZ2l0dWRlcyB0byBiZSBjZW50cmUgb2YgZGlzdHJpYnV0aW9uIG1hcAojIG1lYW4oU0hTX3NpdGVzJG1fbGF0aXR1ZGUpICMgMTMuNDQyOTcKIyBtZWFuKFNIU19zaXRlcyRtX2xvbmdpdHVkZSkgIyAxNDQuNjUyMQoKIyMgY3JlYXRlIG1hcCBzdHlsZSBhdCBodHRwczovL21hcHN0eWxlLndpdGhnb29nbGUuY29tLwpzMjwtICJzdHlsZT1lbGVtZW50Omdlb21ldHJ5JTdDY29sb3I6MHhmNWY1ZjUmc3R5bGU9ZWxlbWVudDpsYWJlbHMlN0N2aXNpYmlsaXR5Om9mZiZzdHlsZT1lbGVtZW50OmxhYmVscy5pY29uJTdDdmlzaWJpbGl0eTpvZmYmc3R5bGU9ZWxlbWVudDpsYWJlbHMudGV4dC5maWxsJTdDY29sb3I6MHg2MTYxNjEmc3R5bGU9ZWxlbWVudDpsYWJlbHMudGV4dC5zdHJva2UlN0Njb2xvcjoweGY1ZjVmNSZzdHlsZT1mZWF0dXJlOmFkbWluaXN0cmF0aXZlJTdDZWxlbWVudDpnZW9tZXRyeSU3Q3Zpc2liaWxpdHk6b2ZmJnN0eWxlPWZlYXR1cmU6YWRtaW5pc3RyYXRpdmUubGFuZF9wYXJjZWwlN0N2aXNpYmlsaXR5Om9mZiZzdHlsZT1mZWF0dXJlOmFkbWluaXN0cmF0aXZlLmxhbmRfcGFyY2VsJTdDZWxlbWVudDpsYWJlbHMudGV4dC5maWxsJTdDY29sb3I6MHhiZGJkYmQmc3R5bGU9ZmVhdHVyZTphZG1pbmlzdHJhdGl2ZS5uZWlnaGJvcmhvb2QlN0N2aXNpYmlsaXR5Om9mZiZzdHlsZT1mZWF0dXJlOnBvaSU3Q3Zpc2liaWxpdHk6b2ZmJnN0eWxlPWZlYXR1cmU6cG9pJTdDZWxlbWVudDpnZW9tZXRyeSU3Q2NvbG9yOjB4ZWVlZWVlJnN0eWxlPWZlYXR1cmU6cG9pJTdDZWxlbWVudDpsYWJlbHMudGV4dC5maWxsJTdDY29sb3I6MHg3NTc1NzUmc3R5bGU9ZmVhdHVyZTpwb2kucGFyayU3Q2VsZW1lbnQ6Z2VvbWV0cnklN0Njb2xvcjoweGU1ZTVlNSZzdHlsZT1mZWF0dXJlOnBvaS5wYXJrJTdDZWxlbWVudDpsYWJlbHMudGV4dC5maWxsJTdDY29sb3I6MHg5ZTllOWUmc3R5bGU9ZmVhdHVyZTpyb2FkJTdDdmlzaWJpbGl0eTpvZmYmc3R5bGU9ZmVhdHVyZTpyb2FkJTdDZWxlbWVudDpnZW9tZXRyeSU3Q2NvbG9yOjB4ZmZmZmZmJnN0eWxlPWZlYXR1cmU6cm9hZCU3Q2VsZW1lbnQ6bGFiZWxzLmljb24lN0N2aXNpYmlsaXR5Om9mZiZzdHlsZT1mZWF0dXJlOnJvYWQuYXJ0ZXJpYWwlN0NlbGVtZW50OmxhYmVscy50ZXh0LmZpbGwlN0Njb2xvcjoweDc1NzU3NSZzdHlsZT1mZWF0dXJlOnJvYWQuaGlnaHdheSU3Q2VsZW1lbnQ6Z2VvbWV0cnklN0Njb2xvcjoweGRhZGFkYSZzdHlsZT1mZWF0dXJlOnJvYWQuaGlnaHdheSU3Q2VsZW1lbnQ6bGFiZWxzLnRleHQuZmlsbCU3Q2NvbG9yOjB4NjE2MTYxJnN0eWxlPWZlYXR1cmU6cm9hZC5sb2NhbCU3Q2VsZW1lbnQ6bGFiZWxzLnRleHQuZmlsbCU3Q2NvbG9yOjB4OWU5ZTllJnN0eWxlPWZlYXR1cmU6dHJhbnNpdCU3Q3Zpc2liaWxpdHk6b2ZmJnN0eWxlPWZlYXR1cmU6dHJhbnNpdC5saW5lJTdDZWxlbWVudDpnZW9tZXRyeSU3Q2NvbG9yOjB4ZTVlNWU1JnN0eWxlPWZlYXR1cmU6dHJhbnNpdC5zdGF0aW9uJTdDZWxlbWVudDpnZW9tZXRyeSU3Q2NvbG9yOjB4ZWVlZWVlJnN0eWxlPWZlYXR1cmU6d2F0ZXIlN0NlbGVtZW50Omdlb21ldHJ5JTdDY29sb3I6MHhjOWM5Yzkmc3R5bGU9ZmVhdHVyZTp3YXRlciU3Q2VsZW1lbnQ6bGFiZWxzLnRleHQuZmlsbCU3Q2NvbG9yOjB4OWU5ZTllJnNpemU9NDgweDM2MCIKCiMjIGdldCBhIEdvb2dsZSBtYXAgb2YgQXByYSBIYXJib3IgKHJldHJpZXZlcyByYXN0ZXIgbWFwIGZyb20gR29vZ2xlIE1hcHMpCiMjIE5vdGUgdGhhdCBpbiBtb3N0IGNhc2VzIGJ5IHVzaW5nIHRoaXMgZnVuY3Rpb24geW91IGFyZSBhZ3JlZWluZyB0byB0aGUgR29vZ2xlIE1hcHMgQVBJIFRlcm1zIG9mIFNlcnZpY2UgYXQgaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL21hcHMtcGxhdGZvcm0vdGVybXMvICoqCkFwcmFIYXJib3IgPC0gZ2V0X2dvb2dsZW1hcChjZW50ZXIgPSBjKGxvbiA9IDE0NC42NTIxLCBsYXQgPSAxMy40NDI5NyksIAogICAgICAgICAgICAgICAgem9vbSA9IDEzLCAKICAgICAgICAgICAgICAgIG1hcHR5cGUgPSAndGVycmFpbicsCiAgICAgICAgICAgICAgICBjb2xvciA9ICdidycsCiAgICAgICAgICAgICAgICBzdHlsZSA9IHMyKQoKIyMgdXNlIGdnbWFwIHRvIHBsb3QgdGhlIHRoZSByYXN0ZXIgdXNpbmcgdGhlIGdncGxvdDIgZnJhbWV3b3JrCmdnbWFwKEFwcmFIYXJib3IpCgojIyBhZGQgU0hTIHN1cnZleSBpbmZvcm1hdGlvbiB0byBwbG90Cm1hcDEuMCA8LSBnZ21hcChBcHJhSGFyYm9yLCBleHRlbnQgPSAicGFuZWwiKSArCiAgIyMgc3BlY2lmeSB0aGUgYWVzdGhldGljcyBvZiB0aGUgImJ1YmJsZXMiIG9uIHRoZSBwbG90CiAgZ2VvbV9wb2ludChhZXMoeCA9IG1fbG9uZ2l0dWRlLCB5ID0gbV9sYXRpdHVkZSwgCiAgICAgICAgICAgICAgICAgZmlsbCA9IHNpdGUsCiAgICAgICAgICAgICAgICAgc2l6ZSA9IG1lYW5fbm9fYmlvX2FtcHMsIHNoYXBlID0gc2hhcGVzKSwgCiAgICAgICAgICAgICBkYXRhID0gU0hTX2ZpZ3VyZV9tYXBfZGF0YSwKICAgICAgICAgICAgIGFscGhhID0gMC42LCAKICAgICAgICAgICAgIHN0cm9rZSA9IDAuNikgKwogICMjIGN1c3RvbWlzZSB0aGUgc2l6ZQogIHNjYWxlX3NpemUocmFuZ2UgPSBjKDIsIDEyKSwgZ3VpZGUgPSAibm9uZSIpICsgCiAgIyMgY3VzdG9taXNlIHRoZSBzaGFwZSBzbyB0aGF0IDAgdmFsdWVzIGFyZSAieCIKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzPWMoMjEsIDQpLCBndWlkZSA9ICJub25lIikgKwogICMgbGltaXQgdGhlIHNpemUgb2YgdGhlIHBsb3QgKHByb2R1Y2VzIG1lc3NhZ2UsIGlnbm9yZSBpdCkKICBzY2FsZV94X2NvbnRpbnVvdXMobGltaXRzID0gYygxNDQuNjE1LCAxNDQuNjkpLCBleHBhbmQgPSBjKDAsIDApKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMTMuNDE1LCAxMy40NyksIGV4cGFuZCA9IGMoMCwgMCkpICsKICB4bGFiKCJMb25naXR1ZGUiKSsKICB5bGFiKCJMYXRpdHVkZSIpKwogICMjIGZhY2V0IHRoZSBwbG90IGJ5IG1vbnRoIChuPTIgZm9yIE1heSBhbmQgSnVseSkKICBmYWNldF93cmFwKH4gY2FsZW5kYXJfbW9udGhfZmFjdG9yX21pZCwgbmNvbCA9IDMpICsKICAjIyBtb3ZlIHRoZSBsZWdlbmQgYW5kIGVkaXQgdGhlIGZhY2V0IHN0cmlwIHBhbmVscwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIiwgCiAgICAgICAgc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwgCiAgICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG91ciA9ICJncmF5MzAiLCBmaWxsID0gTkEpLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4oMC4xLCAwLCAwLjEsIDAsICJjbSIpKSkgKwogICMjIG1ha2UgaXQgY29sb3VyIGJsaW5kIGZyaWVuZGx5CiAgc2NhbGVfZmlsbF92aXJpZGlzKGRpc2NyZXRlPVRSVUUpICsKICAjIyBlZGl0IGxlZ2VuZCBhcHBlYXJhbmNlCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQob3ZlcnJpZGUuYWVzID0gbGlzdChzaGFwZSA9IDIxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gIlNpdGUiKSkgKwogICMjIGFkZCBsYWJlbHMgdG8gImJ1YmJsZXMiCiAgZ2VvbV90ZXh0KGFlcyh4ID0gbV9sb25naXR1ZGUsIHkgPSBtX2xhdGl0dWRlLCAKICAgICAgICAgICAgICAgIGxhYmVsID0gbGFiZWxzLCAKICAgICAgICAgICAgICAgIHNpemUgPSAwLjEpLAogICAgICAgICAgICBkYXRhID0gU0hTX2ZpZ3VyZV9tYXBfZGF0YSkKCiMgbWFwMS4wCgojIHBsb3QubmV3KCkgIyMgdXNlIHRoaXMgaWYgeW91IGdldCBhbiBlcnJvciBiZWxvdwoKIyMgYWRkIGEgc2NhbGUgYmFyCm1hcDEuMiA8LSBtYXAxLjAgKyBzY2FsZWJhcihkYXRhID0gU0hTX2ZpZ3VyZV9tYXBfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3Q9MC41LCAjIGRpc3RhbmNlIHRvIHJlcHJlc2VudCBlYWNoIHNlZ21lbnQgb2YgdGhlIHNjYWxlIGJhciAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RfdW5pdD0ia20iLCAjIHVuaXQgb2YgbWVhc3VyZW1lbnQgZm9yIGRpc3QKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybT1UUlVFLCAjIFRSVUUgPSBkZWNpbWFsIGRlZ3JlZXMsIEZBTFNFID0gbQogICAgICAgICAgICAgICAgICAgICAgICAgICAgbW9kZWwgPSAnV0dTODQnLCAjIGNob2ljZSBvZiBlbGxpcHNvaWRlIG1vZGVsICh3aGVyZSB0cmFuc29ybT1UUlVFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgYm9yZGVyLnNpemUgPSAwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBib3guY29sb3IgPSAiZ3JleTMwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJveC5maWxsID0gYygiZ3JleTgwIiwgIndoaXRlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdC5zaXplID0gMiwgIyMgc2NhbGUgYmFyIHNpemUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBoZWlnaHQgPSAwLjA5LCAjIyBoZWlnaHQgYXMgcHJvcG9ydGlvbiBvZiB0aGUgeS1heGlzICgwLTEpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2NhdGlvbiA9ICJib3R0b21yaWdodCIsICMjIHNjYWxlIGJhcnMgbG9jYXRpb24gaW4gdGhlIHBsb3QKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFuY2hvciA9IGMoeD0xNDQuNjg4LHk9MTMuNDE3KSwgIyMgZm9yIGNvcm5lciBvZiBsb2NhdGlvbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3QuZGlzdCA9IDAuMSwgIyMgZGlzdGFuY2UgYncgc2NhbGUgYmFyIGFuZCB0ZXh0CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IDAgIyMgbWFrZSB0aGUgdGV4dCBzZWUtdGhyb3VnaAogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAKIyBtYXAxLjIKCiMjIGFkZCBub3J0aCBhcnJvdwptYXAxLjMgPC0gbWFwMS4yICsgIAogIGFubm90YXRpb25fbm9ydGhfYXJyb3coaGVpZ2h0ID0gdW5pdCgwLjI1LCAiY20iKSwKICAgICAgICAgICAgICAgICAgICAgICAgIHdpZHRoID0gdW5pdCgwLjI1LCJjbSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgbG9jYXRpb24gPSAidGwiLCAjIyB0b3AgbGVmdAogICAgICAgICAgICAgICAgICAgICAgICAgcGFkX3ggPSB1bml0KDAuMjUsICJjbSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgcGFkX3kgPSB1bml0KDAuMjUsICJjbSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgc3R5bGUgPSBub3J0aF9hcnJvd19vcmllbnRlZXJpbmcoCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmVfd2lkdGggPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lX2NvbCA9ICJncmF5NTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gYygiZ3JheTgwIiwgIndoaXRlIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHRleHRfY29sID0gTkEsICMjIHJlbW92ZXMgdGV4dD8KICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dF9mYW1pbHkgPSAiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dF9mYWNlID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dF9zaXplID0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgdGV4dF9hbmdsZSA9IDApKQptYXAxLjMKIyMgc2F2ZSBpdApnZ3NhdmUoImRvY3MvZmlndXJlX2RldGVjdC50aWZmIiwgcGxvdD1tYXAxLjMsIGRldmljZT0idGlmZiIsIHdpZHRoPTIxLzEuMiwgaGVpZ2h0PTI4LzEuMiwgdW5pdHM9ImNtIiwgZHBpPTUwMCkKYGBgCgpgYGB7ciBidWJibGVfcGxvdF9zdGF0c30KIyMgRk9STUFUIFNIUyBEQVRBIEZPUiBCVUJCTEUgUExPVCBNT0RFTCAtLS0tCiMjIGNyZWF0ZSBkYXRhIGZyYW1lIHdpdGggc2l0ZS1sZXZlbCBkZXRlY3Rpb24gZGF0YSAocmVsZXZhbnQgdG8gbW9kZWwpIG9ubHkKU0hTX2J1YmJsZV9kYXRhIDwtIFNIU19kYXRhX2Z1bGwgJT4lCiAgIyMgZmlsdGVyIG91dCBibGFua3MKICBmaWx0ZXIoIWdyZXBsKCdFJywgc2FtcGxlX2xlc3MpKSAlPiUKICAjIyByZXRhaW4gb25seSBpbmZvcm1hdGlvbiBhdCB0aGUgc2l0ZS1sZXZlbAogIHNlbGVjdChmaWVsZF9jb2xsZWN0aW9uX21vbnRoLCBmY19tb250aF9mYWN0b3IsIGZjX21vbnRoX251bWJlciwKICAgICAgICAgZmNfbW9udGhfbnVtYmVyX2N5Y2xpYywgY2FsZW5kYXJfbW9udGgsIGNhbGVuZGFyX21vbnRoX2ZhY3RvciwKICAgICAgICAgY2FsZW5kYXJfbW9udGhfZmFjdG9yX21pZCwgY2FsZW5kYXJfbW9udGhfbnVtYmVyLCBkYXRlX2NvbGxlY3RlZCwgCiAgICAgICAgIHNpdGUsIHNlYXNvbiwgc2Vhc29uX2ZhY3RvciwgcHJvcF9iaW9fcmVwc19hbXBkX3Blcl9zaXRlLCAKICAgICAgICAgbm9fYmlvX3JlcHNfYW1wZF9wZXJfc2l0ZSwgbm9fYmlvX3JlcHNfY29sbGVjdGVkX3Blcl9zaXRlKSAlPiUKICAjIyBrZWVwIG9ubHkgdW5pcXVlIHJvd3MgKHJlbW92ZSB0ZWNoIHJlcCBpbmZvKQogIHVuaXF1ZSguKSAlPiUKICBtdXRhdGUoc2l0ZV9mYWN0b3I9ZmFjdG9yKHNpdGUpKSAlPiUKICAjIyBjcmVhdGUgbmV3IGNhbGVuZGFyIG1vbnRoIG51bWJlciBjb2x1bW4gCiAgbXV0YXRlKGNhbGVuZGFyX21vbnRoX251bWJlcl9taWQgPSBhcy5udW1lcmljKGNhbGVuZGFyX21vbnRoX2ZhY3Rvcl9taWQpKQoKIyMgZXhwb3J0IGl0CndyaXRlLmNzdihTSFNfYnViYmxlX2RhdGEsICJkb2NzLzAxLjNfU0hTX2J1YmJsZV9kYXRhLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKIyMgTU9ERUwgREFUQSBXSVRIIEdBTSAtLS0tCmdhbV9idWJibGVfY291bnQ8LWdhbShub19iaW9fcmVwc19hbXBkX3Blcl9zaXRlIH4gCiAgICAgICAgICAgICAgICAgIHNpdGVfZmFjdG9yICsgCiAgICAgICAgICAgICAgICAgIHMoY2FsZW5kYXJfbW9udGhfbnVtYmVyX21pZCwgYnM9ImNjIiksCiAgICAgICAgICAgICAgICAgIG9mZnNldCA9IG5vX2Jpb19yZXBzX2NvbGxlY3RlZF9wZXJfc2l0ZSwgIyMgYWxsIGFyZSBuID0gMTAKICAgICAgICAgICAgICAgICAgbWV0aG9kID0gIlJFTUwiLAogICAgICAgICAgICAgICAgICBmYW1pbHkgPSBwb2lzc29uKCksICMjIHN1aXRhYmxlIGZvciBjb3VudCBkYXRhCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBTSFNfYnViYmxlX2RhdGEpCiMjIGNoZWNrIGRpYWdub3N0aWNzCnByaW50KGNoZWNrKGdldFZpeihnYW1fYnViYmxlX2NvdW50KSkpCnN1bW1hcnlfZ2JjPC1zdW1tYXJ5KGdhbV9idWJibGVfY291bnQpCnN1bW1hcnlfZ2JjCgojIyBQYXJhbWV0cmljIGNvZWZmaWNpZW50czoKa25pdHI6OmthYmxlKHN1bW1hcnlfZ2JjJHAudGFibGUsIGRpZ2l0cyA9IDQpCgojIyBBcHByb3hpbWF0ZSBzaWduaWZpY2FuY2Ugb2Ygc21vb3RoIHRlcm1zOgprbml0cjo6a2FibGUoc3VtbWFyeV9nYmMkcy50YWJsZSwgZGlnaXRzID0gNCkKYGBgCgpgYGB7ciB2aXN1YWxpc2VfR0FNLCBmaWcuaGVpZ2h0ID0gNCwgZmlnLndpZHRoID0gOX0KIyMgdmlzdWFsaXNlIEdBTSByZXN1bHRzCnByaW50KGRyYXcoZ2FtX2J1YmJsZV9jb3VudCkpCmBgYAoKYGBge3IgdmlzdWFsaXNlX0dBTV9jdXN0b20sIGZpZy5oZWlnaHQgPSA0LCBmaWcud2lkdGggPSA5LCBpbmNsdWRlPUZBTFNFfQojIyBDVVNUT00gRFJBVyBHQU0gRlVOQ1RJT04gLS0tLQojIyBjcmVhdGUgYSBtb2RpZmllZCB2ZXJzaW9uIG9mIGdyYXRpYSdzIGRyYXcgZnVuY3Rpb24gdXNpbmcgY29kZSBmcm9tIE1hcmNvIFNhbmRyaQojIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy81NDg5MTgyMy9jYW5ub3QtdXBkYXRlLWVkaXQtZ2dwbG90Mi1vYmplY3QtZXhwb3J0ZWQtZnJvbS1hLXBhY2thZ2UtZ3JhdGlhLWluLXIKIyBjdXN0b21fZHJhdyA8LSBmdW5jdGlvbiAob2JqZWN0LCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyYW1ldHJpYyA9IFRSVUUsIHNlbGVjdCA9IE5VTEwsIHNjYWxlcyA9IGMoImZyZWUiLCJmaXhlZCIpLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAiaHYiLCBheGlzID0gImxydGIiLCBuID0gMTAwLCB1bmNvbmRpdGlvbmFsID0gRkFMU0UsIAojICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJhbGxfdW5jZXJ0YWludHkgPSBUUlVFLCBkaXN0ID0gMC4xLCAuLi4pIAojIHsKIyAgIHNjYWxlcyA8LSBtYXRjaC5hcmcoc2NhbGVzKQojICAgUyA8LSBzbW9vdGhzKG9iamVjdCkKIyAgIHNlbGVjdCA8LSBncmF0aWE6OjpjaGVja191c2VyX3NlbGVjdF9zbW9vdGhzKHNtb290aHMgPSBTLCBzZWxlY3QgPSBzZWxlY3QpCiMgICBkIDwtIGdyYXRpYTo6OnNtb290aF9kaW0ob2JqZWN0KQojICAgdGFrZSA8LSBkIDw9IDJMCiMgICBzZWxlY3QgPC0gc2VsZWN0W3Rha2VdCiMgICBTIDwtIFNbdGFrZV0KIyAgIGQgPC0gZFt0YWtlXQojICAgaXNfcmUgPC0gdmFwcGx5KG9iamVjdFtbInNtb290aCJdXSwgZ3JhdGlhOjo6aXNfcmVfc21vb3RoLCBsb2dpY2FsKDFMKSkKIyAgIGlzX2J5IDwtIHZhcHBseShvYmplY3RbWyJzbW9vdGgiXV0sIGdyYXRpYTo6OmlzX2J5X3Ntb290aCwgbG9naWNhbCgxTCkpCiMgICBpZiAoYW55KGlzX2J5KSkgewojICAgICBTIDwtIHZhcHBseShzdHJzcGxpdChTLCAiOiIpLCBgW1tgLCBjaGFyYWN0ZXIoMUwpLCAxTCkKIyAgIH0KIyAgIG5wYXJhIDwtIDAKIyAgIG5zbW9vdGggPC0gbGVuZ3RoKFMpCiMgICBpZiAoaXNUUlVFKHBhcmFtZXRyaWMpKSB7CiMgICAgIHRlcm1zIDwtIHBhcmFtZXRyaWNfdGVybXMob2JqZWN0KQojICAgICBucGFyYSA8LSBsZW5ndGgodGVybXMpCiMgICAgIHAgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gbnBhcmEpCiMgICB9CiMgICBnIDwtIGwgPC0gdmVjdG9yKCJsaXN0IiwgbGVuZ3RoID0gbnNtb290aCkKIyAgIGZvciAoaSBpbiB1bmlxdWUoUykpIHsKIyAgICAgZVMgPC0gZXZhbHVhdGVfc21vb3RoKG9iamVjdCwgc21vb3RoID0gaSwgbiA9IG4sIHVuY29uZGl0aW9uYWwgPSB1bmNvbmRpdGlvbmFsLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIG92ZXJhbGxfdW5jZXJ0YWludHkgPSBvdmVyYWxsX3VuY2VydGFpbnR5LCBkaXN0ID0gZGlzdCkKIyAgICAgbFtTID09IGldIDwtIHNwbGl0KGVTLCBlU1tbInNtb290aCJdXSkKIyAgIH0KIyAgIGwgPC0gbFtzZWxlY3RdCiMgICBkIDwtIGRbc2VsZWN0XQojICAgZyA8LSBnW3NlbGVjdF0KIyAgIGlmIChsZW5ndGgoZykgPT0gMEwpIHsKIyAgICAgbWVzc2FnZSgiVW5hYmxlIHRvIGRyYXcgYW55IG9mIHRoZSBtb2RlbCB0ZXJtcy4iKQojICAgICByZXR1cm4oaW52aXNpYmxlKGcpKQojICAgfQojICAgZm9yIChpIGluIHNlcV9hbG9uZyhsKSkgewojICAgICBnW1tpXV0gPC0gZHJhdyhsW1tpXV0pCiMgICB9CiMgICBpZiAoaXNUUlVFKHBhcmFtZXRyaWMpKSB7CiMgICAgIGZvciAoaSBpbiBzZXFfYWxvbmcodGVybXMpKSB7CiMgICAgICAgcFtbaV1dIDwtIGV2YWx1YXRlX3BhcmFtZXRyaWNfdGVybShvYmplY3QsIHRlcm0gPSB0ZXJtc1tpXSkKIyAgICAgICBnW1tpICsgbGVuZ3RoKGcpXV0gPC0gZHJhdyhwW1tpXV0pCiMgICAgIH0KIyAgIH0KIyAgIGlmIChpc1RSVUUoaWRlbnRpY2FsKHNjYWxlcywgImZpeGVkIikpKSB7CiMgICAgIHdyYXBwZXIgPC0gZnVuY3Rpb24oeCkgewojICAgICAgIHJhbmdlKHhbWyJlc3QiXV0gKyAoMiAqIHhbWyJzZSJdXSksIHhbWyJlc3QiXV0gLSAKIyAgICAgICAgICAgICAgICgyICogeFtbInNlIl1dKSkKIyAgICAgfQojICAgICB5bGltcyA8LSByYW5nZSh1bmxpc3QobGFwcGx5KGwsIHdyYXBwZXIpKSkKIyAgICAgaWYgKGlzVFJVRShwYXJhbWV0cmljKSkgewojICAgICAgIHlsaW1zIDwtIHJhbmdlKHlsaW1zLCB1bmxpc3QobGFwcGx5KHAsIGZ1bmN0aW9uKHgpIHJhbmdlKHhbWyJ1cHBlciJdXSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeFtbImxvd2VyIl1dKSkpKQojICAgICB9CiMgICAgIGdnIDwtIHNlcV9hbG9uZyhnKVtjKGQgPT0gMUwsIHJlcChUUlVFLCBucGFyYSkpXQojICAgICBmb3IgKGkgaW4gZ2cpIHsKIyAgICAgICBnW1tpXV0gPC0gZ1tbaV1dICsgbGltcyh5ID0geWxpbXMpCiMgICAgIH0KIyAgIH0KIyAgIGcKIyB9CiMgCiMgIyMgQ1VTVE9NIFBMT1QgLS0tLQojIHBsb3Q8LWN1c3RvbV9kcmF3KGdhbV9idWJibGVfY291bnQpCiMgCiMgcGxvdDE8LXBsb3RbWzFdXSArIAojICAgZ2d0aXRsZShOVUxMKSArIAojICAgeWxhYigiRWZmZWN0IG9mIG1vbnRoIikgKwojICAgeGxhYigiTW9udGgiKSArIAojICAgdGhlbWVfYncoKSArIAojICAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cz1jKDEsIDEyKSwgCiMgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzPXNlcSgxLCAxMiwgYnk9MSksIAojICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIkp1bHkiLCAiQXVndXN0IiwgIiAgICAgU2VwdGVtYmVyIiwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9jdG9iZXIiLCAiTm92ZW1iZXIiLCAiRGVjZW1iZXIiLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSmFudWFyeSIsICJGZWJydWFyeSIsICJNYXJjaCIsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJBcHJpbCIsICJNYXkiLCAiSnVuZSIpKSArCiMgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0PSAxKSwgCiMgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLng9ZWxlbWVudF9ibGFuaygpKQojIAojIHBsb3QyPC1wbG90W1syXV0gKwojICAgeGxhYigiU2l0ZSIpICsKIyAgIHlsYWIoIlBhcnRpYWwgZWZmZWN0IG9mIHNpdGUiKSArCiMgICBnZ3RpdGxlKE5VTEwpKwojICAgdGhlbWVfYncoKSArCiMgICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscz1jKCJCbHVlIEhvbGUiLCAiSW5uZXIgSGFyYm9yIiwgIk1pZGRsZSBTaG9hbHMiLCAiT3JvdGUgUG9pbnQiLCAiU2FzYSBCYXkiKSkrCiMgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0PSAxKSkrCiMgICBnZW9tX3BvaW50KGZpbGw9dmlyaWRpcyhuPTUpLCBzaXplPTMsIHNoYXBlPTIxLCBzdHJva2U9MC42KQojIAojICMjIHBsb3QgY3VzdG9tIGdhbSBwbG90IGZvciBwdWIKIyBHQU1fcGxvdDwtZ2dhcnJhbmdlKHBsb3QxLCBwbG90MiwgbGFiZWxzPWMoIkEiLCAiQiIpKQojIEdBTV9wbG90CiMgCiMgIyMgc2F2ZSBpdAojIGdnc2F2ZSgiZG9jcy9maWd1cmVfR0FNLnRpZmYiLCBwbG90PUdBTV9wbG90LCBkZXZpY2U9InRpZmYiLCB3aWR0aD0yMTAsIGhlaWdodD0yOTcvMywgdW5pdHM9Im1tIiwgZHBpPTUwMCkKYGBgCgoKCg==